/* 
 *
 * COPYRIGHT NVIDIA CORPORATION 2007. ALL RIGHTS RESERVED.  
 * BY ACCESSING OR USING THIS SOFTWARE, YOU AGREE TO: 
 *
 *  1) ACKNOWLEDGE NVIDIA'S EXCLUSIVE OWNERSHIP OF ALL RIGHTS 
 *     IN AND TO THE SOFTWARE; 
 *
 *  2) NOT MAKE OR DISTRIBUTE COPIES OF THE SOFTWARE WITHOUT 
 *     INCLUDING THIS NOTICE AND AGREEMENT; 
 *
 *  3) ACKNOWLEDGE THAT TO THE MAXIMUM EXTENT PERMITTED BY 
 *     APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND 
 *     THAT NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, 
 *     EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED
 *     TO, IMPLIED WARRANTIES OF MERCHANTABILITY  AND FITNESS 
 *     FOR A PARTICULAR PURPOSE.  
 *
 * IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY 
 * SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES 
 * WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS 
 * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS 
 * INFORMATION, OR ANY OTHER PECUNIARY LOSS), INCLUDING ATTORNEYS'
 * FEES, RELATING TO THE USE OF OR INABILITY TO USE THIS SOFTWARE, 
 * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 */

/*
 *
 * NVidia Corporation
 *
 * Written by Ryan Geiss
 * 
 */

#ifndef NV_DEMO_TEST_H
#define NV_DEMO_TEST_H


#define CLASS_NAME   "Demo Viewer"
#define WINDOW_TITLE "Demo - Generation of Complex Procedural Terrains Using the GPU"

#include "NHelper/DemoSound.h"


// forward class declarations
class NVAnimation;
class NVConstantParam;
class NVProcessedMesh;
//class NVSlider;
class NVSliderManager;
class NVMenuSelector;
class NVSphericalNavigator;
class NVMayaNavigator;
class NVViewer;
class NRVertexBuffer;
class NVMayaTransform;
class NV2DTexture;
class NVStaticMenu;
class NVProc3DTexture;
class NVRenderPass;
class NVConstantBufferRef;
class NVConstantBuffer;
class NVTextureParam;
class NVStreamOutGeometry;

#define CHUNKS_PER_DIM 13 //11   // must be an odd number!

#define NUM_VB    300

#define NUM_LOD 3

typedef enum {
  TB_TRUE,
  TB_FALSE,
  TB_UNKNOWN
} td_tribool;

typedef struct {
  int   i,j,k;   // THESE NEVER CHANGE.
  int   lod;
  int   x,y,z;   // these do change.
  int   vbId;    // note: if you change the x/y/z, be sure to set this to -1!
  float dist_from_cam;  // chunks get sorted by this.
  float opacity;
  td_tribool bHasPolys;
} 
td_chunkinfo;



#include "helpers/support.h"

#include <NVDemo/NVViewer.h>

//--------------------------------------
//
//--------------------------------------
class TerrainDemo : public NVDrawListener, public NVRenderPassListener {
 public:

  // there can be only one...
  static TerrainDemo *s_instance;

  TerrainDemo(const char *args, HINSTANCE hInstance);
  ~TerrainDemo();

  // the main loop func
  void MainLoop();

  void UpdateTime(bool paused, bool slowmo, double animTimeScale, bool bRandomize);  // call this once per frame
    // updates the following values:
    double m_time_pausable;   // pausable and "slowmotionable".   non-regressable.
    double m_non_regressable_anim_time;    // this one should never regress (even on a reset/randomize) (for particles)
    double m_time_since_prog_start;  // for audio
    double m_time_since_last_user_activity;
    double m_dt_undamped;
    double m_dt_unpausable;
    double m_dt_pausable;
    double m_dt_anim;
    float  m_fps;
    // <don't mess with these:>
    double m__prev_frame_raw_time;
    double m_last_mousemove_time;
    double m_last_mousedrag_time;
    double m_last_keydown_time;
    bool   m_bDontUpdateFpsNextFrame;

  double m_timeScale;    // use this to scale sense of time for the *entire* demo
  int  m_nFrame;  // frames since program start.  (vs. m_nAnimFrame, which changes on preset load)
  int  m_nAnimFrame;  // frames since last preset load or anim start; can regress/reset to zero.

  void UpdateCamera();

  HWND m_hWndConfigDialog;
  static INT_PTR CALLBACK ConfigProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); //DLGPROC

  bool m_bConfigMode;
  // config panel settings:
  bool m_bUserOverride;
  bool m_bWidescr;
  int m_nFullscreenWidth;
  int m_nFullscreenHeight;
  int m_nAA;         // 0/1=1, 2=2, 4=4
  bool m_bVSync;
  bool m_bHelpButton;
  int  m_nUberQuality; // 0/1/2
  void LoadConfig();
  void SaveConfig();

  // SETTINGS:
  NVec2i m_winDimensions;
  NVec2i m_renDimensions;
  float  m_pixelCorrection;
  uint   m_numSamples;
  float  m_fSuperSampling;
  bool   m_bFlushOnNonSLI;
  float  m_quadScaleFactor;   // 16:9 correction factor

  // INIT FUNCTIONS:
  void ApplyWinDimensions  (NVScene* scene);
  void UpdateWinDimensions (NVScene* scene);
  void InitDemo(NVScene* scene);
  void Consume(NVScene* scene, const std::string& Filename );
  void PreSetSceneInitDemo(NVScene* scene);
  void PostSetSceneInitDemo(NVScene* scene);


  // STATE STUFF:
  bool m_bCursorShowing;
  bool m_bLastMouseKnown;
  int  m_nLastMouseX;
  int  m_nLastMouseY;
  bool m_wireframe;
  bool m_Paused; // is the demo paused?
  bool m_SlowMotion;
  bool m_ctrlIsDown;
  bool m_shiftIsDown;
  bool m_showingCursor;

  void ResetUserActivityTimer(); // resets the timer that triggers AutoPilot mode
  void Pause(bool doPause, bool force = false);
  void SetWireframe(bool wireframe);   


  // navigators that we build and the viewer we set it on...
  NVViewer             *m_viewer;
  NVNavigator          *m_navigator;
  NVSphericalNavigator *m_sphericalNavigator;
  
  // the 'main' render pass that renders to the screen
  NVRenderPass *m_primaryPass;

  // the list of active animations
  //std::list<NVAnimation*> m_animations;

 public:

  //
  // static window handler methods that we give NRender
  //
  static void MouseButtonCB (int button, int state, int x, int y){
    s_instance->MouseButton(button, state, x, y);
  }
  static void MouseDragCB (int x, int y){
    s_instance->MouseDrag(x, y);
  }
  static void MouseMoveCB (int x, int y){
    s_instance->MouseMove(x, y);
  }
  static void ResizeCB (int w, int h){
    s_instance->Resize(w, h);
  }
  static void KeyUpCB (uchar key, int x, int y){
    s_instance->KeyUp(key, x, y);
  }
  static void KeyDownCB (uchar key, int x, int y){
    s_instance->KeyDown(key, x, y);
  }
  static void UpdateCB (){
    s_instance->Update();
  }

  //
  // The actual handler method that is non-static
  //
  void MouseButton (int button, int state, int x, int y);
  void MouseDrag   (int x, int y);
  void MouseMove   (int x, int y);
  void Resize      (int w, int h);
  void KeyUp       (uchar key, int x, int y);
  void KeyDown     (uchar key, int x, int y);
  void Update      ();  
 
  // method called to quit everything
  void Quit();

public:

  virtual void OnDraw(NVViewer*, NVRenderSettings*, NVShape *);
  virtual void OnRenderPass(NVViewer*, bool bStarting, NVRenderPass*);

  bool     m_prog_initialized;
  int      m_preset_initialized;


  // MENUS, SLIDERS:
  NVStaticMenu* m_pTopMenu;
  NVStaticMenu* m_pSliderMenu;
  NVSliderManager *m_sliderManager;
  NVMenuSelector  *m_menuSelector;
  std::string      m_sliderFile;
  void  InitMenuAndSliders(NVScene* scene);
  void  ApplySlider(int index, float &stackVar);


  // LOGO BADGE, HELP/ABOUT BOXES, 'CLICK HERE FOR HELP' MSG:
  bool m_bShowLogo;
  float m_fHelpScrOpacity;
  float m_fAboutBoxOpacity;
  
  void  UpdateHelpScreen();
  void  UpdateAboutBox();
  void  InitLogo();
  void  InitClickHelp();
  void  UpdateClickHelpAndLogo();


  // AUDIO:
  DemoSoundFmod* AddLoopingSound(char* filename, float vol);
  std::vector<DemoSoundFmod*> m_sounds;   //aliased

  // SCREENSHOTS:
  bool m_bCaptureFrames; // forces frame time to 1/30 of a second
  int  m_nCaptureTiling;
  bool m_bDrawingTiledScreenShot;

  void    saveCurrentFrameToNumberedFile();
  NImage* ScreenShotTiled(NVRenderPass *renderPass, int tile_w, int tile_h, int rows, int cols, int channels);
  void    ScreenShot(int tiling);
  void    SaveFrameBuffer(const std::string& filename);

  // RANDOM NUMBERS:
  DWORD  m_rand_seeds[64];
  double m_rand[8192];
  void GenerateRandNumbersFromSeeds();

  float m_fChunkLog;
  int  m_nVoxelDim;
  int  m_nMargin;
  int  m_nVoxelDimPlusMargins;
  int  m_wsChunkSize[3];
  int  m_num_vb;

  // cached stuff:
  NVRenderPass* m_build_density_pass;
  NVRenderPass* m_list_nonempty_cells_pass;
  NVRenderPass* m_list_verts_to_generate_pass;
  NVRenderPass* m_splat_vertex_IDs_pass;
  NVRenderPass* m_gen_vertices_pass;
  NVRenderPass* m_gen_indices_pass;
  NVShape* m_build_density_shape;
  NVShape* m_list_nonempty_cells_shape;
  NVShape* m_list_verts_to_generate_shape;
  NVShape* m_splat_vertex_IDs_shape;
  NVShape* m_gen_vertices_shape;
  NVShape* m_gen_indices_shape;
  
  std::vector<NVConstantBufferRef*> m_lod_CB_refs;
  NVConstantBuffer* m_lod_CBs[3];
  NVConstantBuffer* m_ChunkCB;

  void Randomize();
  NPnt3f GetGeomCenter(int lod);

  // LODs: 0=lowest..2=highest

  void ClearChunk(int lod, int i, int j, int k);
  td_chunkinfo m_chunks[3][CHUNKS_PER_DIM][CHUNKS_PER_DIM][CHUNKS_PER_DIM];
    td_chunkinfo* m_sortedChunks[CHUNKS_PER_DIM*CHUNKS_PER_DIM*CHUNKS_PER_DIM*3];
    int           m_nSortedChunks;
    void SortChunks(td_chunkinfo** data, int nValues);
    td_chunkinfo* m_tempChunks[CHUNKS_PER_DIM*CHUNKS_PER_DIM*CHUNKS_PER_DIM*3];
  
    float GetChunkLOD(NVec3f chunkCenter, NPnt3f camPos);
  int BuildLodChunkNOW(NVec3f wsChunkPos, int lod, int vbId);

  NVStreamOutGeometry* m_vb_list[NUM_VB];
  NVStreamOutGeometry* m_ib_list[NUM_VB];
  int         m_free_vb_list[NUM_VB];
  int         m_num_free_vbs;

  bool m_bViewCull;
  bool m_bFreezeTess;

  float m_camForward;

};

#endif // NV_DEMO_TEST_H
